Чтение данных¶
Для обращения к данным в хранилище Tarantool Column Store используются SQL-запросы.
TCS поддерживает следующие способы обращения к данным:
SQL-запросы
SELECTчерез HTTP API – наиболее простой способ, подходящий для работы без нагрузки, например, для тестирования и отладки или единичного выполнения.Потоковые SQL-запросы – безопасный способ получать большие объемы данных без риска выхода за пределы памяти на стороне хранилища.
Аналитические расчеты – хранимые в TCS именованные процедуры, в которых доступен широкий спектр операций над данными с использованием всех поддерживаемых функций и операторов SQL.
Представления для чтения (read views) – снимки хранилища данных в конкретный момент времени.
Нематериализованные SQL-представления (non-materialized views in SQL) – удобный способ обращаться к большим запросам, как к таблице.
Поддерживаемые операторы и функции SQL описаны в Справочнике по SQL.
SELECT-запросы через HTTP¶
Для выполнения запроса SELECT отправьте POST-запрос с его текстом на HTTP-адрес /sql.
Пример запроса на чтение всех объектов таблицы:
POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json
SELECT * FROM db.public.user
Пример запроса на чтение с фильтром:
POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json
SELECT * FROM db.public.users WHERE id=1
Пример чтения из схемы по умолчанию tcs.schema:
POST http://localhost:7777/sql HTTP/1.1
Content-Type: application/json
SELECT * FROM users WHERE id=1
Потоковые SQL-запросы¶
Потоковые SQL-запросы позволяют делать масштабные выборки, которые возвращают настолько большой объем данных, что в случае обычных SELECT-запросов для формирования ответа не хватило бы памяти на стороне хранилища. При обработке потоковых запросов хранилище не формирует ответ целиком перед отправкой, а возвращает его по частям. Большого объема памяти при этом не требуется.
Для выполнения потокового запроса отправьте POST-запрос с его текстом на HTTP-адрес /streaming/sql.
Примечание
Обратите внимание, что для обработки запросов на HTTP-адресе /streaming/sql также используется
отдельный сервер, чей адрес указывется в конфигурационном параметре
http_listen_streaming (по умолчанию 0.0.0.0:7877).
Пример потокового запроса на чтение всех объектов таблицы:
POST http://localhost:7877/streaming/sql HTTP/1.1
Content-Type: application/json
SELECT * FROM db.public.user
Примечание
Ограничения в текущей версии TCS:
Просмотр списка активных запросов и отмена запроса средствами TCS не поддерживаются. При необходимости такой запрос можно отменить, закрыв HTTP-соединение.
Отправка запросов в отдельную среду выполнения не поддерживается. Запросы на этом HTTP-адресе выполняюся только в среде выполнения
streaming.
Аналитические расчеты¶
TCS позволяет выполнять аналитические расчеты над хранимыми данными. Аналитический расчет – это процедура, которая содержит агрегирующие запросы к таблицам и колонкам и возвращает результат выполнения запросов. В аналитических расчетах можно использовать поддерживаемые операторы и функции SQL, описанные в Справочнике по SQL.
Аналитические расчеты являются хранимыми объектами: чтобы выполнить расчет, нужно сначала его создать и загрузить в TCS. После этого он станет доступен для вызова через HTTP API по имени. Это позволяет уменьшить накладные расходы, возникающие при отправке SQL-запросов через HTTP. Среди таких расходов: сериализация и десериализация, передача по сети, разбор и составление плана запроса. Таким образом, аналитические расчеты – оптимальный механизм для ресурсоемких запросов, повторяющихся многократно.
Примечание
Ограничения:
Для управления аналитическими расчетами используется только HTTP-адрес /computation.
В качестве аналитического расчета используется один подготовленный оператор SQL (prepared statement).
Созданные аналитические расчеты нельзя изменить. Вместо этого удалите существующий расчет и создайте заново.
Создание аналитических расчетов¶
Чтобы создать аналитический расчет, отправьте POST-запрос на HTTP-адрес /computation.
Тело запроса должно содержать JSON-объект с одним полем sql. В качестве его значения
используйте SQL-оператор PREPARE, создающий подготовленное SQL-выражение (prepared statement).
Пример: расчет количества записей в таблице.
POST http://localhost:7777/computation HTTP/1.1
{
"sql": "PREPARE plan(INT) AS SELECT count(*) FROM db.public.users WHERE age > $1"
}
В качестве параметров для подготовленных SQL-выражений используйте типы данных PostgreSQL.
Выполнение аналитических расчетов¶
Чтобы выполнить существующий в TCS расчет, отправьте POST-запрос на HTTP-адрес /computation/run.
JSON-тело запроса должно содержать массив пар имя расчета:аргументы.
Пример:
POST http://localhost:7777/computation/run HTTP/1.1
[
{
"name": "compute1",
"args": [ 1 ]
},
{
"name": "compute2",
"args": [ 2 ]
}
]
В ответе TCS отправляет результаты выполнения в следующих секциях:
success– результаты успешно выполненных запросов;fail– информация о запросах, которые не удалось выполнить;timings– время, потраченное на разные этапы обработки запроса;plans– вывод EXPLAIN ANALYZE для планов, указанных в заголовке запроса.
Подробнее см. Просмотр фактического времени выполнения запроса и Ограничение времени выполнения запроса.
Также для аналитических расчетов доступны следующие возможности:
Управление аналитическими расчетами¶
Чтобы просмотреть все доступные расчеты, отправьте запрос GET на HTTP-адрес /computation:
GET http://localhost:7777/computation HTTP/1.1
Чтобы просмотреть текст конкретного расчета, отправьте запрос GET на HTTP-адрес /computation/<NAME>:
GET http://localhost:7777/computation/plan HTTP/1.1
Чтобы удалить запрос, отправьте запрос DELETE на HTTP-адрес /computation/<NAME>:
DELETE http://localhost:7777/computation/plan HTTP/1.1
Представления для чтения (read views)¶
TCS предполагает работу под постоянной транзакционной нагрузкой на запись. Для консистентности чтения данных в таких условиях TCS использует представления для чтения (read view) – снимки хранилища данных в конкретный момент времени.
В каждый момент времени в системе существует одно актуальное представление на чтение.
Оно обновляется раз в .aggregator.rv_update_ms миллисекунд.
Также есть очередь из используемых в данный момент представлений, из которых еще выполняется чтение в проходящих обработку запросах SQL и прочих.
Когда запрос на чтение обработан, и при этом уже есть более актуальное представление,
то представление, которые использовалось для этого запроса, помещается в очередь.
При следующей итерации цикла сборщика мусора (раз в .aggregator.rv_update_ms миллисекунд)
все представления из очереди удаляются.
Подробнее о представлениях на чтение читайте в документации Tarantool EE.
Нематериализованные SQL-представления (non-materialized views in SQL)¶
Нематериализованные SQL-представления удобны тем, что позволяют обращаться к большим запросам, как к таблице. Поскольку создаваемое представление не имеет физической формы, указанный запрос будет выполняться каждый раз при обращении к этому представлению.
Создание нематериализованных SQL-представлений¶
Чтобы создать нематериализованное SQL-представление, воспользуйтесь любым из двух способов:
Отправьте запрос
POSTна HTTP-адрес/ddl/sql. Тело запроса должно содержать SQL-операторCREATE OR REPLACE VIEW.Отправьте запрос
POSTна HTTP-адрес видаddl/view/<NAME>/create, например/ddl/view/adults/create. Тело запроса должно содержать JSON-объект с одним полемsql. В качестве его значения используйте SQL-операторSELECT(неCREATE OR REPLACE VIEW).
Пример (SQL-представление, содержащее список имен всех совершеннолетних лиц в таблице):
на HTTP-адрес
/ddl/sql:POST http://localhost:7777/ddl/sql CREATE OR REPLACE VIEW adults AS SELECT name FROM db.public.users WHERE age >= 18;
на HTTP-адрес вида
/ddl/view/<NAME>/create:POST http://localhost:7777/ddl/view/adults/create { "sql": "SELECT name FROM db.public.users WHERE age >= 18;" }
Примечание
Созданные SQL-представления существуют только на мастер-узле хранилища Tarantool и не сохраняются между перезагрузками.
Использование нематериализованных SQL-представлений¶
После создания нематериализованное SQL-представление становится доступно:
для запросов через HTTP-адрес
/sql,для аналитических расчетов через HTTP-адрес
/computation,для использования в других нематериализованных SQL-представлениях; это означает, что в TCS можно создавать вложенные (иерархические) нематериализованные SQL-представления.
Пример: обращение к SQL-представлению adults из SELECT-запроса.
SELECT salary FROM db.public.users WHERE name IS IN (SELECT name FROM adults)
Управление нематериализованными SQL-представлениями¶
Чтобы просмотреть все доступные нематериализованные SQL-представления, отправьте запрос GET на HTTP-адрес /ddl/view:
GET http://localhost:7777/ddl/view HTTP/1.1
Чтобы просмотреть текст конкретного SQL-представления, отправьте запрос GET на HTTP-адрес вида ddl/view/<NAME>, например:
GET http://localhost:7777/ddl/view/adults HTTP/1.1
Чтобы заместить существующее SQL-представление, отправьте запрос POST
с инструкцией вида CREATE OR REPLACE VIEW name[(column_name [, ...])] AS query
на HTTP-адрес ddl/sql, например:
POST http://localhost:7777/ddl/sql HTTP/1.1
CREATE OR REPLACE VIEW adults AS SELECT name FROM db.public.users WHERE age >= 16
Чтобы удалить SQL-представление, воспользуйтесь любым из двух способов:
Отправьте запрос
POSTна HTTP-адрес/ddl/sql. Тело запроса должно содержать SQL-операторDELETE VIEWс указанием имени представления, например:POST http://localhost:7777/ddl/sql HTTP/1.1 DROP VIEW adults
Отправьте пустой запрос
POSTна HTTP-адрес видаddl/view/<NAME>/delete, например/ddl/view/adults/delete:POST http://localhost:7777/ddl/view/adults/delete HTTP/1.1
Оптимизация запросов на чтение шардированных данных¶
В TCS предусмотрены правила оптимизации для запросов на чтение шардированных данных:
RemotePushDownLocalizedPrepared
Правило RemotePushDown¶
Правило оптимизации RemotePushDown позволяет исполнить всю возможную часть запроса
на одном шарде, для того чтобы по сети пересылался минимум данных. Без этой оптимизации
все данные из таблицы, необходимые для выполнения запроса, пересылались бы по сети
на экземпляр Scheduler, где дальше исполнялся бы сам запрос.
Далее рассмотрим на примерах, как это правило оптимизации влияет на обработку шардированных
запросов на чтение. Предположим, что у нас есть шардированная таблица t(a, b) и 3 шарда.
Простое полное сканирование (full scan)¶
Рассмотрим простой запрос:
SELECT * FROM t
Запросив EXPLAIN на экземпляре Scheduler, можно увидеть физический план следующего вида:
RemoteExec: table=datafusion.public.t, partitioning=Hash([a@0], 3), filter=None
UnionExec
TarantoolExec: table=datafusion.public.t fields=[a, b], ...
BufExec: table=datafusion.public.t fields=[a, b], ...
В этом плане есть узел RemoteExec, который отправляет нижележащий план на шарды
для последующего исполнения. Тот план, который он отправляет, назовем удаленным планом.
В нашем примере удаленный план – это план с корневым узлом UnionExec.
В описании RemoteExec мы видим следующее:
есть соответствие между этим узлом и таблицей
t(table=);партицирование данных производится по хэшу колонки
a(согласно схеме шардирования);используется пустой фильтр.
В нашем примере таблица будет просканирована на 3 шардах, а все записи отправлены на экземпляр Scheduler и там объединены.
Одношардовый перебор¶
Рассмотрим более сложный запрос, где для выполнения не обязательно читать данные со всех шардов:
SELECT * FROM t WHERE a = 1
Здесь мы получаем физический план следующего вида:
RemoteExec: table=datafusion.public.t, partitioning=Hash([a@0], 1), filter=datafusion.public.t.a=1
CoalesceBatchesExec: target_batch_size=8192
FilterExec: a@0 = 1
UnionExec
TarantoolExec: table=datafusion.public.t fields=[a, b], ...
BufExec: table=datafusion.public.t fields=[a, b], ...
В описании RemoteExec мы видим следующее:
весь план проталкивается внутрь (push down) через
RemoteExec;фильтрация выполняется полностью в рамках одного шарда, а по сети пересылаются уже отфильтрованные данные.
Здесь правило RemotePushDown пытается поднять RemoteExec как можно выше.
Для того чтобы посмотреть, как отработало то или иное правило оптимизации, можно
использовать запрос EXPLAIN VERBOSE. Он возвращает состояния плана после каждой
изменившей его оптимизации. Например, в нашем случае можно видеть, что первоначальный
физический план имеет такой вид:
FilterExec: a@0 = 1
RemoteExec: table=datafusion.public.t, partitioning=Hash([a@0], 1), filter=datafusion.public.t.a=1
UnionExec
TarantoolExec: table=datafusion.public.t fields=[a, b], ...
BufExec: table=datafusion.public.t fields=[a, b], ...
Здесь фильтрация осуществляется после сбора данных с шардов, а после прохода
правила оптимизации RemotePushDown фильтр проталкивается вниз через RemoteExec,
вследствие чего запрос становится лучше локализован в рамках шарда.
Исполнение частичной агрегации на шардах¶
Правило RemotePushDown также может обеспечить выполнение частичной агрегации на шардах.
Рассмотрим запрос:
SELECT count(*) FROM t
Он имеет следующий физический план:
AggregateExec: mode=Final, gby=[], aggr=[count(*)]
CoalescePartitionsExec
RemoteExec: partitioning=UnknownPartitioning(3), filter=None
AggregateExec: mode=Partial, gby=[], aggr=[count(*)]
UnionExec
TarantoolExec: table=datafusion.public.t fields=[], ...
BufExec: table=datafusion.public.t fields=[], ...
Как мы видим, частичная агрегация была перенесена на шард. Заметим, что в этом примере
для RemoteExec уже не выводится table, а также используется UnknownPartitioning
вместо Hash. Это происходит из-за того, что после исполнения агрегации изменяется
выходная схема: поле a в ней больше не присутствует, но появляется поле count(*).
То, какие узлы можно проталкивать внутрь для исполнения на одном шарде, определяется следующими факторами:
Если в запросе указан фильтр, который читает лишь одно значение ключа шардирования (например,
a = $1), то запрос может быть полностью локализован и план будет полностью протолкнут подRemoteExec.В ином случае проталкивание производится до тех пор, пока шард может исполнить соответствующий узел самостоятельно (то есть пока для сбора данных шарду не нужны данные с других шардов). Например,
CoalescePartitionsExecне может быть исполнен локально, поскольку для него необходимо собрать данные со всех шардов, тогда какFilterExecможет быть исполнен локально.
Правило LocalizedPrepared¶
Правило оптимизации LocalizedPrepared позволяет полностью предотвратить передачу
физического плана с экземпляра Scheduler на экземпляр Aggregator при исполнении
обоих следующих условий:
Исполняется аналитический запрос (prepared statement на HTTP-адрес
/computation/run).План был полностью локализован.
В таком случае при исполнении аналитического запроса будет использоваться план следующего вида:
PreparedStatementExec: name=query, mode=local {from_cache: true}
RemoteExec: table=datafusion.public.sharded_by_indexed_string, partitioning=Hash([a@0], 1), filter=None
PreparedStatementExec: name=query, mode=remote, metrics=[]\n
Данный план следует читать так:
«Исполняется аналитический запрос, локальный для экземпляра Scheduler.
Его план состоит из однопартицированного RemoteExec, который отправляет
удаленный план на экземпляр Aggregator для исполнения аналитического запроса.»
В этом случае вместо тяжелого плана будет отослан всего один узел (PreparedStatementExec).
Далее экземпляр Aggregator сам запланирует аналитический запрос с этим названием
(либо возьмет план для исполнения из кэша) и исполнит его.